Twitterカード自動生成ページ

画像URL欄とサイトURL欄を分けて、同じ行番号どうしで組み合わせて個別ZIPを生成します。
1行目の画像URLは、右側1行目のサイトURLと組み合わされます。
行数が違う場合は、少ない方の件数まで生成します。
画像件数: 0
URL件数: 0
生成件数: 0
状態: 待機中
プレビュー
左の画像URL一覧と右のサイトURL一覧を、上から順番に組み合わせます

移動中です。開かない場合は こちら

`; } function renderPreview() { const pairs = parseInput(); els.count.textContent = pairs.length; els.status.textContent = pairs.length ? '確認OK' : 'データなし'; if (!pairs.length) { els.list.innerHTML = '
ここにプレビューが表示されます。
'; return pairs; } els.list.innerHTML = pairs.map((p, i) => `
preview ${i+1}
${i + 1}. ${escapeHtml(els.title.value || '※すぐ消す')}
画像URL: ${escapeHtml(p.image)}
サイトURL: ${escapeHtml(p.target)}
`).join(''); return pairs; } function downloadBlob(filename, blob) { const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = filename; document.body.appendChild(a); a.click(); setTimeout(() => { URL.revokeObjectURL(a.href); a.remove(); }, 1000); } async function generateIndividualZips() { const pairs = renderPreview(); if (!pairs.length) return alert('データがありません。'); const title = els.title.value.trim() || '※すぐ消す'; const prefix = (els.prefix.value.trim() || 'twitter_card_').replace(/[\\/:*?"<>|]/g, '_'); els.status.textContent = '生成中...'; for (let i = 0; i < pairs.length; i++) { const zip = new JSZip(); zip.file('index.html', buildHtml(title, pairs[i].image, pairs[i].target)); const blob = await zip.generateAsync({ type: 'blob' }); downloadBlob(`${prefix}${i + 1}.zip`, blob); } els.status.textContent = '個別ZIP生成完了'; } async function generateMergedZip() { const pairs = renderPreview(); if (!pairs.length) return alert('データがありません。'); const title = els.title.value.trim() || '※すぐ消す'; const prefix = (els.prefix.value.trim() || 'twitter_card_').replace(/[\\/:*?"<>|]/g, '_'); els.status.textContent = 'まとめZIP生成中...'; const root = new JSZip(); for (let i = 0; i < pairs.length; i++) { const inner = new JSZip(); inner.file('index.html', buildHtml(title, pairs[i].image, pairs[i].target)); const innerBlob = await inner.generateAsync({ type: 'uint8array' }); root.file(`${prefix}${i + 1}.zip`, innerBlob); } const blob = await root.generateAsync({ type: 'blob' }); downloadBlob(`${prefix}all.zip`, blob); els.status.textContent = 'まとめZIP生成完了'; } els.parseBtn.addEventListener('click', renderPreview); els.downloadAllBtn.addEventListener('click', generateIndividualZips); els.downloadMergedBtn.addEventListener('click', generateMergedZip); els.sampleBtn.addEventListener('click', () => { els.images.value = `https://i.imgur.com/BRmkQfE.png https://i.imgur.com/tBSXet6.jpeg https://i.imgur.com/Q3l1vK1.png`; els.targets.value = `https://cado-f5i.pages.dev/ https://lite.tiktok.com/t/ZS9dnsocyTwn8-w1azM/ https://matching-affi.jp/r/ag7u2m80`; renderPreview(); }); els.clearBtn.addEventListener('click', () => { els.images.value = ''; els.targets.value = ''; renderPreview(); }); [els.images, els.targets, els.title].forEach(el => { el.addEventListener('input', () => { clearTimeout(window.__pvTimer); window.__pvTimer = setTimeout(renderPreview, 180); }); }); renderPreview();